1   /************************************************************
2   *                     Copyright                            *
3   * Portions of this software are Copyright (c) 1993 - 2002, *
4   * Chad Z. Hower (Kudzu) and the Indy Pit Crew              *
5   *  - http://www.nevrona.com/Indy/                          *
6   ************************************************************/
7   package org.indy;
8   
9   import java.io.ByteArrayInputStream;
10  import java.io.ByteArrayOutputStream;
11  import java.io.File;
12  import java.io.FileInputStream;
13  import java.io.FileNotFoundException;
14  import java.io.IOException;
15  import java.io.InputStream;
16  import java.io.UnsupportedEncodingException;
17  
18  import org.indy.io.IOHandler;
19  import org.indy.io.IndyIOException;
20  import org.indy.io.MaxLineLengthExceededException;
21  import org.indy.io.NotConnectedException;
22  import org.indy.io.PeerDisconnectedException;
23  import org.indy.io.ReadTimedOutException;
24  
25  import org.indy.util.IndyUtilities;
26  import org.indy.util.StringList;
27  
28  
29  /***
30   * Provides extended functionality for using {@link org.indy.IOHandler}s,
31   * such as reading and writing primitives, checking response codes and
32   * extracting RFC compliant messages.
33   *
34   *@author    Owen Green
35   */
36  public class Connection extends IndyComponent {
37    /***
38   * The IOHandler used by this connection
39   */
40    protected IOHandler ioHandler;
41  
42    /***
43   * Holds the result of the last command for reply checking
44   */
45    protected RFCReply lastCmdResult = new RFCReply();
46  
47    /***
48   *  Constructs a new instance of <code>TCPConnection</code>
49   *
50   * @param handler The IOHandler for this connection to use
51   */
52    public Connection(IOHandler handler) {
53      super();
54      ioHandler = handler;
55    }
56  
57    /***
58   *  Sets the maxLineLength attribute of the TCPConnection object
59   *
60   *@param  newLen  The new maxLineLength value
61   */
62    public void setMaxLineLength(int newLen) {
63      ioHandler.setMaximumLineLength(newLen);
64    }
65  
66    /***
67   *  Sets the timeout for read operations.
68   *
69   *@param  timeout   The new readTimeOut value
70   */
71    public void setReadTimeOut(int timeout) throws IndyIOException {
72      ioHandler.setReadTimeOut(timeout);
73    }
74  
75    /***
76   *  Sets the iOHandler attribute of the IdTCPConnection object
77   *
78   *@param  handler  The new iOHandler value
79   */
80    public void setIOHandler(IOHandler handler) {
81      if (handler == null) {
82        throw new NullPointerException();
83      }
84  
85      ioHandler = handler;
86    }
87  
88    /***
89   *  Gets the maxLineLength attribute of the IdTCPConnection object
90   *
91   *@return    The maxLineLength value
92   */
93    public int getMaximumLineLength() {
94      return ioHandler.getMaximumLineLength();
95    }
96  
97    /***
98   *  Returns the timeout for read operations.
99   *
100  *@return     The readTimeOut value
101  *@returns    The timeout for read operations, in milliseconds.
102  */
103   public int getReadTimeOut() throws IndyIOException {
104     return ioHandler.getReadTimeOut();
105   }
106 
107   /***
108  *  Checks whether this TCP connection is still connected.
109  *
110  *@return   <code>true</code> if the connection is still active, false otherwise.
111  *@throws  ConnectionClosedGracefullyException  If the connection closes gracefully
112  */
113   public boolean isConnected() {
114     return (ioHandler == null) ? false : ioHandler.isConnected();
115   }
116 
117   /***
118  *  Gets the iOHandler attribute of the TCPConnection object
119  *
120  *@return    The iOHandler value
121  */
122   public IOHandler getIOHandler() {
123     return ioHandler;
124   }
125 
126   /***
127  *
128  * Reads lines from the connection into <code>dest</code> until <code>delim</code> is hit,
129  * and returns the number of lines read into <code>dest</code>
130  *
131  * @param dest The {@link Captureable} to add lines to
132  * @param delim The delimiter to used demarcate the message
133  * @param isRFCMessage Whether the incoming text should be treated as part of an RFC message
134  * @return The number of lines read
135  * @throws IndyIOException if an IO error occurs
136  * @throws ReadTimedOutException if the read operation times out
137  * @throws PeerDisconnectedException if the remote connection disconnects
138  * @throws MaxLineLengthExceededException if the maximum line lentgh is exceeded
139  * @throws NotConnectedException if not connected
140  */
141   protected final int performCapture(Capturable dest, String delim, 
142                                      boolean isRFCMessage)
143                               throws IndyIOException, ReadTimedOutException, 
144                                      PeerDisconnectedException, 
145                                      MaxLineLengthExceededException, 
146                                      NotConnectedException {
147     if (dest == null) {
148       throw new NullPointerException();
149     }
150 
151     int lineCount = 0;
152 
153     doBeginWork(IndyComponent.WorkMode.READ);
154 
155     try {
156       while (true) {
157         String s = readLine();
158 
159         if (s.equals(delim)) {
160           break;
161         }
162 
163         lineCount++;
164 
165         //For RFC 822 retrives
166         if (isRFCMessage && s.startsWith("..")) {
167           s = s.substring(1);
168         }
169 
170         dest.addLine(s);
171       }
172     }
173      finally {
174       doEndWork(IndyComponent.WorkMode.WRITE);
175 
176       return lineCount;
177     }
178   }
179 
180   /***
181  * Reads lines of text from the connection until into a {@link org.indy.util.StringList}
182  * delim is found and returns the number of lines read.
183  *
184  * @param strings The <code>StringList</code> to fill
185  * @param delim The delimter to read up to
186  * @param isRFC Whether the text should be interpreted as part of an RFC message
187  * @return The number of lines read
188  * @throws IndyIOException if an IO error occurs
189  * @throws ReadTimedOutException if the read operation times out
190  * @throws PeerDisconnectedException if the remote peer disconnects
191  * @throws MaxLineLengthExceededException if the maximum line length is exceeded
192  * @throws NotConnectedException if not connected
193  */
194   public final int capture(final StringList strings, String delim, 
195                            boolean isRFC) throws IndyIOException, 
196                                                  ReadTimedOutException, 
197                                                  PeerDisconnectedException, 
198                                                  MaxLineLengthExceededException, 
199                                                  NotConnectedException {
200     Capturable c = new Capturable() {
201       public void addLine(String s) {
202         strings.add(s);
203       }
204     };
205 
206     return performCapture(c, delim, isRFC);
207   }
208 
209   /***
210  * Reads lines of text from the connection into a <code>StringBuffer</code>
211  * until <code>delim</code> is found and returns the number of lines read.
212  *
213  * @param strings The <code>StringBuffer</code> to fill
214  * @param delim the delimiter to look for
215  * @param isRFC <code>true</code> if this should be treated as an RFC message
216  * @return the number of lines read
217  * @throws IndyIOException if an IO error occurs
218  * @throws ReadTimedOutException if the read operation times out
219  * @throws PeerDisconnectedException if the remote peer disconnects
220  * @throws MaxLineLengthExceededException if the maximum line length is exceeded
221  * @throws NotConnectedException if not connected
222  */
223   public final int capture(final StringBuffer strings, String delim, 
224                            boolean isRFC) throws IndyIOException, 
225                                                  ReadTimedOutException, 
226                                                  PeerDisconnectedException, 
227                                                  MaxLineLengthExceededException, 
228                                                  NotConnectedException {
229     Capturable c = new Capturable() {
230       public void addLine(String s) {
231         strings.append(s);
232         strings.append('\n');
233       }
234     };
235 
236     return performCapture(c, delim, isRFC);
237   }
238 
239   /***
240  * Resets this connection
241  */
242   protected void resetConnection() {
243     if (ioHandler != null) {
244       ioHandler.clearBuffer();
245     }
246   }
247 
248   /***
249  * Disconnects this connection.
250  *
251  * @throws IndyIOException If an IO error occurs
252  */
253   public void disconnect() {
254     doStatus(Status.DISCONNECTING);
255     disconnectSocket();
256 
257 
258     //  ioHandler = null;
259     doStatus(Status.DISCONNECTED);
260   }
261 
262   /***
263  *
264  */
265   public void disconnectSocket() {
266     if (ioHandler != null) {
267       ioHandler.close();
268     }
269   }
270 
271   /***
272  * Checks whether a numeric response code is within a desired
273  * list of responses. If so, the response is returned otherwise
274  * a {@link ProtocolException} is thrown.
275  *
276  * @param response The response being examined
277  * @param allowedResponses The allowed responses
278  * @return The response encountered
279  * @throws ProtocolException if the response is not in the allowed list
280  */
281   public int checkResponse(int response, int[] allowedResponses)
282                     throws ProtocolException {
283     if (allowedResponses == null) {
284       throw new NullPointerException();
285     }
286 
287     if (allowedResponses.length > 0) {
288       for (int i = 0; i < allowedResponses.length; i++) {
289         if (response == allowedResponses[i]) {
290           return response;
291         }
292       }
293 
294       throw new ProtocolException(lastCmdResult);
295     }
296 
297     return response;
298   }
299 
300   /***
301  * Reads a response from the remote peer, typically
302  * of the form of a response code and some text,
303  * and checks that the code is within <code>allowedResponses</code>.
304  *
305  * If not a {@link ProtocolException} is thrown, otherwise the response
306  * encountered is returned.
307  *
308  * @param allowedResponses The response codes allowed
309  * @return The encountered response
310  * @throws PeerDisconnectedException If the remote host disconnects
311  * @throws ReadTimedOutException If the read operation times out
312  * @throws IndyIOException If an IO error occurs
313  * @throws ProtocolException If the response is not in allowed set
314  * @throws MaxLineLengthExceededException If the maximum line length is exceeded
315  * @throws NotConnectedException If not connected
316  */
317   public int getResponse(int[] allowedResponses)
318                   throws PeerDisconnectedException, ReadTimedOutException, 
319                          IndyIOException, ProtocolException, 
320                          MaxLineLengthExceededException, NotConnectedException {
321     getInternalResponse();
322 
323     return checkResponse(lastCmdResult.getNumericCode(), allowedResponses);
324   }
325 
326   /***
327  * Retreives a response from the connection using {@link getInternalResponse}
328  * and checks it for validity. This is an alias for {@link getResponse(int[])}
329  *
330  * @param allowedResponse The expected response
331  * @return The response, if found
332  * @throws PeerDisconnectedException if the remote peer disconnected
333  * @throws ReadTimedOutException if the read operation timed out
334  * @throws IndyIOException if an IO error occured
335  * @throws ProtocolException if the desired response was not returned
336  * @throws MaxLineLengthExceededException if the maximum line length was exceeded
337  * @throws NotConnectedException if not connected
338  */
339   public int getResponse(int allowedResponse) throws PeerDisconnectedException, 
340                                                      ReadTimedOutException, 
341                                                      IndyIOException, 
342                                                      ProtocolException, 
343                                                      MaxLineLengthExceededException, 
344                                                      NotConnectedException {
345     return getResponse(new int[] { allowedResponse });
346   }
347 
348   /***
349  * Used to implement the processing required for {@link getResponse(int[])}.
350  * A response, typically expected to be comptrised of a three digit code and
351  * some text is read and stored using {@link readLineWait()}
352  *
353  * @throws IndyIOException if an IO error occurs
354  * @throws ReadTimedOutException if the read operation times out
355  * @throws PeerDisconnectedException if the remote peer disconnectes
356  * @throws MaxLineLengthExceededException if the maximum line length is exceeded
357  * @throws NotConnectedException if not connected
358  */
359   protected void getInternalResponse() throws IndyIOException, 
360                                               ReadTimedOutException, 
361                                               PeerDisconnectedException, 
362                                               MaxLineLengthExceededException, 
363                                               NotConnectedException {
364     StringList response = new StringList();
365     String line = readLineWait();
366 
367     response.add(line);
368 
369     if (line.length() > 3) {
370       if (line.charAt(4) == '-') {
371         String term = line.substring(1, 4) + " ";
372 
373         while ((line.length() > 4) && !line.substring(1, 5).equals(term)) {
374           line = readLineWait();
375           response.add(line);
376         }
377       }
378     }
379 
380     lastCmdResult.parseResponse(response);
381   }
382 
383   /***
384  * Invokes {@link readLine} and will allow upto
385  * <code>failCount</code> time outs before throwing
386  * {@link ReadTimedOutException}.
387  *
388  * @param failCount The number of time outs to allow before failing
389  * @return A line of text from the connection.
390  * @throws IndyIOException If an IO error occurs.
391  * @throws ReadTimedOutException If the operation times out more than <code>failCount</code> times
392  * @throws PeerDisconnectedException If the remote peer disconnects
393  * @throws MaxLineLengthExceededException If the maximum line length is exceeded
394  * @throws NotConnectedException If not connected
395  */
396   public String readLineWait(int failCount) throws IndyIOException, 
397                                                    ReadTimedOutException, 
398                                                    PeerDisconnectedException, 
399                                                    MaxLineLengthExceededException, 
400                                                    NotConnectedException {
401     StringBuffer sb = new StringBuffer();
402     int attempts = 0;
403 
404     while ((sb.length() == 0) && (attempts < failCount)) {
405       attempts++;
406 
407       try {
408         sb.append(readLine());
409       }
410        catch (ReadTimedOutException ex) {
411         if (attempts >= failCount) {
412           throw ex;
413         }
414       }
415     }
416 
417     return sb.toString();
418   }
419 
420   /***
421    * DOCUMENT ME!
422    *
423    * @return DOCUMENT ME!
424    *
425    * @throws IndyIOException DOCUMENT ME!
426    * @throws ReadTimedOutException DOCUMENT ME!
427    * @throws PeerDisconnectedException DOCUMENT ME!
428    * @throws MaxLineLengthExceededException DOCUMENT ME!
429    * @throws NotConnectedException DOCUMENT ME!
430    */
431   public String readLineWait() throws IndyIOException, ReadTimedOutException, 
432                                       PeerDisconnectedException, 
433                                       MaxLineLengthExceededException, 
434                                       NotConnectedException {
435     return readLineWait(Integer.MAX_VALUE);
436   }
437 
438   /***
439  * Reads a line of text from the connection.
440  * This wraps {@link org.indy.io.IOHandler#readLine()}/
441  *
442  * @return A line of text from the connection
443  * @throws PeerDisconnectedException If the remote peer disconnects
444  * @throws ReadTimedOutException If the read operation times out
445  * @throws IndyIOException If an IO error occurs
446  * @throws MaxLineLengthExceededException If the maximum line length is exceeded
447  * @throws NotConnectedException If not connected
448  */
449   public String readLine() throws PeerDisconnectedException, 
450                                   ReadTimedOutException, IndyIOException, 
451                                   MaxLineLengthExceededException, 
452                                   NotConnectedException {
453     return ioHandler.readLine();
454   }
455 
456   /***
457  *Reads a line of text from the connection. This wraps
458  * {@link org.indy.io.IOHandler#readLine(int,int)}
459  *
460  * @param timeout The timeout to allow for this read operation
461  * @param maxLineLength The maximum line lentgh to accept
462  * @return A line of text from this connection
463  * @throws IndyIOException If an IO error occurs
464  * @throws ReadTimedOutException If the operation times out
465  * @throws PeerDisconnectedException If the remote peer disonnects
466  * @throws MaxLineLengthExceededException If the maximum line length is exceeded
467  * @throws NotConnectedException If not connected
468  */
469   public String readLine(int timeout, int maxLineLength)
470                   throws IndyIOException, ReadTimedOutException, 
471                          PeerDisconnectedException, NotConnectedException, 
472                          MaxLineLengthExceededException {
473     if (!isConnected()) {
474       throw new NotConnectedException(IndyUtilities.getResourceString(
475                                           "RSNotConnected"));
476     }
477 
478     return ioHandler.readLine(timeout, maxLineLength, null);
479   }
480 
481   /***
482  * Fills a buffer, <code>b</code>, with up to <code>len</code> bytes of
483  * data from the connection.
484  *
485  * @param b The buffer to fill
486  * @param len The amount of data to read
487  * @return The actual amount of data read
488  * @throws PeerDisconnectedException If the remote peer disconnects
489  * @throws ReadTimedOutException If the read operation times out
490  * @throws IndyIOException If an IOError occurs
491  * @throws NotConnectedException if not connected
492  */
493   public final int readBuffer(byte[] b, int len)
494                        throws PeerDisconnectedException, ReadTimedOutException, 
495                               IndyIOException, NotConnectedException {
496     if (b == null) {
497       throw new NullPointerException(IndyUtilities.getResourceString(
498                                          "RSNullBuffer"));
499     }
500 
501     if ((len > b.length) || (len < 1)) {
502       throw new IllegalArgumentException(IndyUtilities.getResourceString(
503                                              "RSBufferBoundsFail"));
504     }
505 
506     return ioHandler.read(b, 0, len);
507   }
508 
509   /***
510  * Reads an <code>int</code> from this connection.
511  *
512  * @return An <code>int</code>
513  * @throws PeerDisconnectedException If the remote peer disconnects
514  * @throws ReadTimedOutException If the read operation timed out
515  * @throws IndyIOException If an IO error occurs.
516  * @throws NotConnectedException If not connected
517  */
518   public final int readInt() throws PeerDisconnectedException, 
519                                     ReadTimedOutException, IndyIOException, 
520                                     NotConnectedException {
521     byte[] b = new byte[4];
522 
523     ioHandler.read(b, 0, 4);
524 
525     return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3];
526   }
527 
528   /***
529  * Reads a <code>long</code> from the connection
530  *
531  * @return A <code>long</code> from the connection
532  * @throws PeerDisconnectedException If the remote peer disconnects
533  * @throws ReadTimedOutException If the read operation times out
534  * @throws NotConnectedException If not connected
535  * @throws IndyIOException If an IO error occurs
536  */
537   public final long readLong() throws PeerDisconnectedException, 
538                                       ReadTimedOutException, IndyIOException, 
539                                       NotConnectedException {
540     byte[] buf = new byte[8];
541 
542     ioHandler.read(buf, 0, 8);
543 
544     return ((buf[0] & 0xFFL) << 56) + ((buf[1] & 0xFFL) << 48) + 
545            ((buf[2] & 0xFFL) << 40) + ((buf[3] & 0xFFL) << 32) + 
546            ((buf[4] & 0xFFL) << 24) + ((buf[5] & 0xFFL) << 16) + 
547            ((buf[6] & 0xFFL) << 8) + ((buf[7] & 0xFFL) << 0);
548   }
549 
550   /***
551  * Read a <code>char</code> from the connection
552  *
553  * @return A <code>char</code> from the connection
554  * @throws PeerDisconnectedException If the remote peer disconnects
555  * @throws ReadTimedOutException If the read operation times out
556  * @throws NotConnectedException If not connected
557  * @throws IndyIOException If an IO error occurs
558  */
559   public final char readChar() throws PeerDisconnectedException, 
560                                       ReadTimedOutException, IndyIOException, 
561                                       NotConnectedException {
562     byte[] b = new byte[2];
563 
564     readBuffer(b, 2);
565 
566     return (char) ((b[0] << 8) + (b[1] << 0));
567   }
568 
569   /***
570  * Reads a short from the connection
571  *
572  * @return A <code>short</code> from the connection
573  * @throws PeerDisconnectedException If the remote peer disconnects
574  * @throws ReadTimedOutException If the read operation times out
575  * @throws NotConnectedException If not connected
576  * @throws IndyIOException If an IO error occurs
577  */
578   public final short readShort() throws PeerDisconnectedException, 
579                                         ReadTimedOutException, IndyIOException, 
580                                         NotConnectedException {
581     return (short) readChar();
582   }
583 
584   /***
585  * Reads bytes from the socket and returns them in an InputStream.
586  * If readUntilDisconnect is true, then the method will block and return only when the connection has closed (or if an exception is thrown).
587  * If readUntilDisconnect is false, then byteCount bytes will be read.
588  * If readUntilDisconnect is false and byteCount = -1 then the first 4 bytes read from the socket will be assumed to be an integer representing the stream size, and this number of bytes will be read.
589  *
590  * @param byteCount The number of bytes to read
591  * @param readUntilDisconnect Whether <code>byteCount</code> should be ignored and the read operation should contniue until disconnected
592  * @return An <code>InputStream</code> containing bytes from the connection.
593  * @throws PeerDisconnectedException If the remote peer disconnects
594  * @throws ReadTimedOutException If the read operation times out
595  * @throws NotConnectedException If not connected
596  * @throws IndyIOException If an IO error occurs
597  */
598   public final InputStream readStream(int byteCount, 
599                                       boolean readUntilDisconnect)
600                                throws PeerDisconnectedException, 
601                                       ReadTimedOutException, 
602                                       NotConnectedException, IndyIOException {
603     if ((byteCount == -1) && (!readUntilDisconnect)) {
604       /*** @todo This should probably nuke if the result is negative */
605       byteCount = readInt();
606     }
607 
608     if (readUntilDisconnect) {
609       return new ByteArrayInputStream(readTillDisconnect());
610     }
611     else {
612       byte[] bytes = new byte[byteCount];
613 
614       readBuffer(bytes, byteCount);
615 
616       return new ByteArrayInputStream(bytes);
617     }
618   }
619 
620   /***
621  * Write the contents of a {@link org.indy.util.StringList} instance
622  * to this connection.
623  *
624  * This method ensures that individual lines in <code>strings</code> beginning
625  * with the response termination character '.' are converted to '..',
626  * and call WriteLn using the updated values. When all lines have been
627  * written, WriteRFCStrings calls WriteLn to send the '.' termination
628  * character to indicate the end of RFC response messages
629  *
630  * @param strings A <code>StringList</code> to write
631  * @throws IndyIOException If an IO error occurs
632  * @throws NotConnectedException If not connected
633  */
634   public final void writeRFCStrings(StringList strings)
635                              throws IndyIOException {
636     for (int i = 0, n = strings.size(); i < n; i++) {
637       if (strings.get(i).equals(".")) {
638         writeLine("..");
639       }
640       else {
641         writeLine(strings.get(i));
642       }
643     }
644 
645     writeLine(".");
646   }
647 
648   /***
649  * Write an {@link org.indy.RFCReply} to this connection.
650  *
651  * @param reply The <code>RFCReply</code> to write
652  * @throws IndyIOException If an IO error occurs
653  * @throws NotConnectedException If not connected
654  */
655   public final void writeRFCReply(RFCReply reply) throws IndyIOException {
656     if (reply.replyExists()) {
657       write(reply.generateReply());
658     }
659   }
660 
661   /***
662  * Write a <code>String</code> to this connection.
663  *
664  * @param s The <code>String</code> to write
665  * @throws IndyIOException If an IO error occurs
666  * @throws NotConnectedException If not connected
667  */
668   public final void write(String s) throws IndyIOException, 
669                                            NotConnectedException {
670     if ((s != null) && (s.length() > 0)) {
671       writeBuffer(s.getBytes());
672     }
673   }
674 
675   /***
676  * Write a buffer of bytes to this connection.
677  *
678  * @param buf The bytes to write
679  * @throws IndyIOException If an IO error occurs
680  * @throws NotConnectedException If not connected
681  */
682   public final void writeBuffer(byte[] buf) throws IndyIOException, 
683                                                    NotConnectedException {
684     if (!isConnected()) {
685       throw new NotConnectedException(IndyUtilities.getResourceString(
686                                           "RSNotConnected"));
687     }
688 
689     if ((buf != null) && (buf.length > 0)) {
690       /*
691  *  This could possibly do with changing?
692  */
693       try {
694         for (int i = 0; i < buf.length; i++) {
695           ioHandler.write(buf[i]);
696           doWork(IndyComponent.WorkMode.WRITE, i);
697         }
698 
699         ioHandler.flush();
700       }
701        catch (IndyIOException ioe) {
702         disconnectSocket();
703         throw ioe;
704       }
705     }
706   }
707 
708   /***
709  * Write a line of text to the connection.
710  *
711  * @param line The text to write as a line
712  * @throws IndyIOException If an IO error occurs
713  * @throws NotConnectedException If not connected
714 */
715   public final void writeLine(String line) throws IndyIOException {
716     /*** @todo Fix to use prop EOL */
717     write(line + "\n");
718     ioHandler.flush();
719   }
720 
721   /***
722  * Write the contents of a {@link org.indy.util.StringList}
723  * as an RFC header, translating strings of the form
724  * <pre>name=value</pre>
725  * to
726  * <pre>name:value</pre>
727  *
728  *
729  * @todo This seems a silly way of doing it - why not use <code>java.util.Map</code>?
730  * @param header The <code>StringList</code> to write as a header
731  * @throws IndyIOException If an IO error occurs
732  * @throws NotConnectedException If not connected
733  */
734   public final void writeHeader(StringList header) throws IndyIOException {
735     if (header == null) {
736       throw new NullPointerException();
737     }
738 
739     for (int i = 0, n = header.size(); i < n; i++) {
740       String line = header.get(i);
741       int pos = line.indexOf('=');
742 
743       writeLine(line.substring(0, pos) + ": " + line.substring(pos + 1));
744     }
745   }
746 
747   /***
748  * @throws IndyIOException If an IO error occurs
749  * @throws NotConnectedException If not connected
750  *
751  * @param value The <code>int</code> to write
752  * @throws IndyIOException If an IO error occurs
753  * @throws NotConnectedException If not connected
754  */
755   public final void writeInt(int value) throws IndyIOException {
756     ioHandler.write((value >>> 24) & 0xFF);
757     ioHandler.write((value >>> 16) & 0xFF);
758     ioHandler.write((value >>> 8) & 0xFF);
759     ioHandler.write((value >>> 0) & 0xFF);
760   }
761 
762   /***
763  * Write a <code>long</code> to the connection
764  *
765  * @param value The <code>long</code> to write
766  * @throws IndyIOException If an IO error occurs
767  * @throws NotConnectedException If not connected
768  */
769   public final void writeLong(long value) throws IndyIOException {
770     byte[] buf = new byte[8];
771     int mask = 0xFF;
772 
773     for (int i = 0; i < 8; i++) {
774       buf[i] = (byte) ((value & (mask << (8 * i))) >> (8 * i));
775     }
776 
777     writeBuffer(buf);
778   }
779 
780   /***
781  * Write a <code>short</code> to the connection
782  * @param value The <code>short</code> to write
783  * @throws IndyIOException If an IO error occurs
784  * @throws NotConnectedException If not connected
785  */
786   public final void writeShort(short value) throws IndyIOException {
787     writeBuffer(
788         new byte[] { (byte) ((value & 0xFF00) >> 8), (byte) (value & 0xFF) });
789   }
790 
791   /***
792  * Write a the contents of a {@link org.indy.util.StringList} instance
793  * to the connection as individual lines.
794  *
795  * @param strings The <code>StringList</code> to write
796  * @param writeLineCount Whether to prepend the text with a int representing the number of lines
797  * @throws IndyIOException If an IO error occurs
798  * @throws NotConnectedException If not connected
799  */
800   public final void writeStrings(StringList strings, boolean writeLineCount)
801                           throws IndyIOException {
802     if (writeLineCount) {
803       writeInt(strings.size());
804     }
805 
806     for (int i = 0, n = strings.size(); i < n; i++) {
807       writeLine(strings.get(i));
808     }
809   }
810 
811   /***
812  * Writes the contents of an <code>InputStream</code> to the connection
813  *
814  * @param stream The <code>InputStream</code> to write
815  * @param len The number of bytes to write
816  * @param writeByteCount Whether the bytes should be prepended with an int representing the byte count
817  * @throws IndyIOException If an IO error occurs
818  * @throws NotConnectedException If not connected
819  */
820   public final void writeStream(InputStream stream, int len, 
821                                 boolean writeByteCount)
822                          throws IndyIOException {
823     doBeginWork(IndyComponent.WorkMode.WRITE, len);
824 
825     try {
826       if (writeByteCount) {
827         writeInt(len);
828       }
829 
830       /*** @todo Must do better - this will be horrible for large data sets */
831       byte[] buf = new byte[len];
832 
833       stream.read(buf);
834       writeBuffer(buf);
835     }
836      catch (IOException ioe) {
837       throw new IndyIOException(ioe);
838     }
839      finally {
840       doEndWork(IndyComponent.WorkMode.WRITE);
841     }
842   }
843 
844   /***
845  * Writes a <code>File</code> object to the stream
846  *
847  * @param f The <code>File</code> to write
848  * @throws FileNotFoundException If the <code>File</code> doesn't exist
849  * @throws IndyIOException If an IO error occurs
850  * @throws NotConnectedException If not connected
851  */
852   public final void writeFile(File f) throws FileNotFoundException, 
853                                              IndyIOException {
854     if (!f.exists()) {
855       throw new FileNotFoundException(f.getName());
856     }
857 
858     writeStream(new FileInputStream(f), (int) f.length(), true);
859   }
860 
861   /***
862  * Returns the current data that can be read without blocking.
863  *
864  * This wraps {@link org.indy.io.IOHandler#currentReadBuffer()}
865  *
866  * @return The data from the read buffer
867  * @throws PeerDisconnectedException If the remote peer has disconnected
868  * @throws ReadTimedOutException If the read operation times out
869  * @throws IndyIOException If an IO error occurs
870  * @throws NotConnectedException If not connected
871  */
872   public final byte[] currentReadBuffer() throws PeerDisconnectedException, 
873                                                  IndyIOException {
874     return ioHandler.currentReadBuffer();
875   }
876 
877   /***
878    * DOCUMENT ME!
879    *
880    * @return DOCUMENT ME!
881    *
882    * @throws PeerDisconnectedException DOCUMENT ME!
883    * @throws ReadTimedOutException DOCUMENT ME!
884    * @throws IndyIOException DOCUMENT ME!
885    */
886   protected byte[] readTillDisconnect() throws PeerDisconnectedException, 
887                                                ReadTimedOutException, 
888                                                IndyIOException {
889     doBeginWork(IndyComponent.WorkMode.READ);
890 
891     boolean timeoutChanged = false;
892     final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
893 
894     try {
895       while (true) {
896         try {
897           bytes.write(currentReadBuffer());
898         }
899          catch (PeerDisconnectedException e) {
900           break;
901         }
902          catch (IOException ex) {
903           throw new IndyIOException(ex);
904         }
905       }
906 
907       return bytes.toByteArray();
908     }
909      finally {
910       doEndWork(IndyComponent.WorkMode.READ);
911     }
912   }
913 
914   /***
915  * Reads all data from the connection until disconnection occurs,
916  * and returns it as a <code>String</code> using the platform character
917  * encoding.
918  *
919  * This is equivalent to <code>allData(null)</code>.
920  *
921  * @return A string representing all read data from the connection
922  * @throws ReadTimedOutException If the read operation times out
923  * @throws IndyIOException If an IO error occurs
924  * @throws NotConnectedException If not connected
925  * @see allData(String)
926  */
927   public String allData() throws ReadTimedOutException, IndyIOException, 
928                                  NotConnectedException {
929     return allData(null);
930   }
931 
932   /***
933  * Reads all data from the connection until disconnection occurs,
934  * and returns it as a <code>String</code> using the character
935  * encoding specified by <code>encoding</code>, or the platform default
936  * if <code>encoding</code> is <code>null</code>.
937  *
938  * @param encoding The character encoding to use
939  * @return A string representing all read data from the connection
940  * @throws IndyIOException If an IO error occurs
941  * @throws ReadTimedOutException If the read operation times out
942  * @throws NotConnectedException If not connected
943  */
944   public String allData(String encoding) throws IndyIOException, 
945                                                 ReadTimedOutException, 
946                                                 NotConnectedException {
947     try {
948       return (encoding == null)
949              ? new String(readTillDisconnect())
950              : new String(readTillDisconnect(), encoding);
951     }
952      catch (UnsupportedEncodingException ex) {
953       throw new IndyIOException(ex);
954     }
955   }
956 
957   /***
958  * An interface that supports having lines of text input to it.
959  */
960   protected interface Capturable {
961     void addLine(String s);
962   }
963 }
This page was automatically generated by Maven